<template>
  <a-spin :spinning="loading">
    <div class="running-detail-container" ref="runningDetailRef">
      <div v-if="isEmpty(pipelineInstance)" class="empty-dom">
        <EmptyPipeline text="当前流水线尚未运行" btn-text="立即运行" @on-click="runPipeline" :showIcon="false" />
      </div>
      <template v-else>
        <div class="header">
          <div>
            <div class="item">
              <SvgIcon
                :name="pipelineStatusMap[pipelineInstance.pipelineStatus]?.icon"
                :style="{ marginRight: '8px', color: pipelineStatusMap[pipelineInstance.pipelineStatus]?.color }"
                :class="{ 'svg-rotate': pipelineInstance.pipelineStatus == PipelineStatus.running }"
              />
              <span :style="{ color: pipelineStatusMap[pipelineInstance.pipelineStatus]?.color }">{{
                pipelineStatusMap[pipelineInstance.pipelineStatus]?.text
              }}</span>
              <div class="divider"></div>
              <div>
                <span class="label">构建号：</span>
                <span>{{ pipelineInstance.buildNumber }}</span>
              </div>
            </div>
            <div class="item">
              <div>
                <span class="label">触发方式：</span>
                <span>{{ triggerTypeMap[pipelineInstance.triggerType] }}</span>
              </div>
              <div class="divider"></div>
              <div>
                <span class="label">发布人：</span>
                <span>{{ pipelineInstance.creator }}</span>
              </div>
            </div>
            <div class="item">
              <div>
                <span class="label">触发时间：</span>
                <span>{{ formatToDatetime(pipelineInstance.startTime) }}</span>
              </div>
              <div class="divider"></div>
              <div>
                <span class="label">持续时间：</span>
                <span>{{ continueTime }}</span>
              </div>
            </div>
            <div class="item">
              <div>
                <span class="label">环境变量：</span>
                <span class="function-value" @click="handelCheckVariable">{{ variableSettings?.length || 0 }}</span>
              </div>
            </div>
          </div>
          <a-button
            class="terminate-btn"
            v-if="pipelineInstance.pipelineStatus === PipelineStatus.running"
            :loading="stopLoading"
            @click="stop"
          >
            <template #icon>
              <SvgIcon name="terminate" :style="{ color: 'rgba(230, 55, 28)' }" />
            </template>
            终止
          </a-button>
        </div>
        <div class="content-box">
          <div class="content">
            <template v-for="(tasks, groupIndex) in computedTasks" :key="groupIndex">
              <div class="task-group">
                <template
                  v-for="(item, index) in orderBy(tasks || [], (t: Pipeline.RunningTask) => t.sortIndex)"
                  :key="item.taskUuid"
                >
                  <div
                    class="task-item"
                    :class="{
                  'linked-group': groupIndex < computedTasks.length - 1 && index == 0,
                  'lined-item': index < tasks!.length - 1,
                  'no-radius': index > 0,
                }"
                  >
                    <div class="border-preffix" :style="{ background: taskStatusMap[item.taskStatus]?.color }"></div>
                    <div class="top-box">
                      <div class="title">
                        <a-tooltip :title="taskStatusMap[item.taskStatus]?.text" placement="top">
                          <SvgIcon
                            :name="taskStatusMap[item.taskStatus]?.icon"
                            :style="{
                              color: taskStatusMap[item.taskStatus]?.color,
                              marginRight: '8px',
                              fontSize: '16px',
                            }"
                          />
                        </a-tooltip>

                        {{ item.name }}
                      </div>
                      <div class="time-and-tools">
                        <div class="time">{{ item.elapsedTime }}</div>
                        <div class="tool-btn">
                          <template v-for="data in orderBy(item.action, 'index')" :key="data.actionName">
                            <div @click="handleAction(data)">
                              <SvgIcon :name="data.icon" :style="{ color: 'rgba(0, 0, 0, 0.6)', width: '12px' }" />
                              {{ data.actionName }}
                            </div>
                          </template>
                        </div>
                      </div>
                    </div>
                    <div class="bottom-box">
                      <template v-for="result in item.resultInfo" :key="result.type">
                        <div class="error-tip" v-if="result.type == ResultInfoType.errorTip">
                          {{ (result.data as Pipeline.ErrorTip[])[0].tip }}
                        </div>
                        <div class="bottom-info" v-if="result.type == ResultInfoType.bottomInfo">
                          <template v-for="data in result.data" :key="data.label">
                            <div class="bottom-info-item">
                              <div :style="{ color: (data as Pipeline.BottomInfo)?.color }">
                                {{ (data as Pipeline.BottomInfo).value }}
                              </div>
                              <div>{{ (data as Pipeline.BottomInfo).label }}</div>
                            </div>
                          </template>
                        </div>
                        <div class="invoke" v-if="result.type == ResultInfoType.invoke">
                          <template v-for="data in result.data" :key="data.buttonName">
                            <div :style="{ color: (data as Pipeline.InvokeBtn)?.color }" @click="handleInvoke(data)">
                              {{ (data as Pipeline.InvokeBtn).buttonName }}
                            </div>
                          </template>
                        </div>
                        <div class="invoke" v-if="result.type == ResultInfoType.checkPoint">
                          <div>
                            {{ (result.data as Pipeline.CheckPoint[])[0].name }}
                            <SvgIcon
                              :style="{ color: item.taskStatus == TaskStatus.success ? '#1da50a' : '#ee7663' }"
                              :name="item.taskStatus == TaskStatus.success ? 'pass-icon' : 'no-pass-icon'"
                            />
                          </div>
                        </div>
                      </template>
                    </div>
                  </div>
                </template>
              </div>
            </template>
          </div>
        </div>
      </template>
      <TaskLogModal ref="taskLogModalRef" />
      <EnvironmentVariableModal ref="environmentVariableModalRef" :list="variableSettings" />
      <TriggerPipelineModal ref="triggerPipelineModal" @handle-success="handleTriggerPipelineSuccess" />
    </div>
  </a-spin>
</template>
<script setup lang="ts">
import { isEmpty, orderBy } from 'lodash';
import { firstTaskParentUuid, pipelineStatusMap, taskStatusMap, triggerTypeMap } from '@/settings/commonSettings';
import { judgeArrResult, getTime } from '@/utils';
import { TaskStatus, PipelineStatus, ResultInfoType, ActionType } from '@/enums/pipelineEnum';
import { defHttp } from '@/utils/http';
import { message } from 'ant-design-vue';
import TaskLogModal from './components/taskLogModal.vue';
import EnvironmentVariableModal from './components/environmentVariableModal.vue';
import { getPipelineInstanceInfo, stopPipeline } from '@/api/pipeline';
import { formatToDatetime } from '@/utils/DateUtil';
import TriggerPipelineModal from '@/views/applicationManagement/components/modules/tables/triggerPipelineModal.vue';

const props = defineProps({
  pipelineCode: {
    type: String,
    default: '',
  },
  flag: {
    type: Number,
  },
});
const route = useRoute();
const runningDetailRef = ref();
const triggerPipelineModal = ref();
const pipelineInstance = ref<Pipeline.PipelineInstance>({} as Pipeline.PipelineInstance);
const variableSettings = ref<Pipeline.VariableSetting[]>([]);
const pipelineData = ref<Pipeline.RunningTask[]>([]);
const loading = ref(false);
const stopLoading = ref(false);
let timer: any = null;
let loop: any = null;
let taskTimer: any = null;
const taskLogModalRef = ref();
const computedTasks = computed(() => {
  if (pipelineData.value.length == 0) return [];
  const groupObj = Object.groupBy(pipelineData.value, (item: Pipeline.RunningTask) => item.parentUuid || '');
  const arr = Object.values(groupObj);
  const orderArr = [arr.find((item: any) => item[0].parentUuid == firstTaskParentUuid)];
  let index = 0;
  while (index < arr.length) {
    index++;
    const nextbling = arr.find((item: any) => {
      const previous_task_arr = orderArr[orderArr.length - 1]?.map((i: Pipeline.RunningTask) => i.taskUuid) || [];
      return judgeArrResult(item[0].parentUuid?.split(',') || [], previous_task_arr);
    });
    if (nextbling) {
      orderArr.push(nextbling);
    }
  }
  return orderArr;
});

const continueTime = ref();

const getContinueTime = () => {
  if (pipelineInstance.value.endTime) {
    clearInterval(timer);
  }
  continueTime.value = getTime(pipelineInstance.value.startTime, pipelineInstance.value.endTime);
};

//运行
const runPipeline = async () => {
  triggerPipelineModal.value.showModal({ pipelineCode: route.params.code });
};

watch(
  () => pipelineInstance.value,
  () => {
    if (pipelineInstance.value.pipelineStatus === PipelineStatus.running) {
      if (loop) return;
      loop = setInterval(() => {
        fetchPipelineInfo();
      }, 3000);
    } else {
      clearAllInterval();
    }
  },
  { immediate: true },
);

onMounted(async () => {
  loading.value = true;
  await init();
});

onBeforeUnmount(() => {
  clearAllInterval();
});

function clearAllInterval() {
  timer && clearInterval(timer);
  loop && clearInterval(loop);
  taskTimer && clearInterval(taskTimer);
}

const fetchPipelineInfo = async () => {
  try {
    const data: Pipeline.PipelineRunningInfo = await getPipelineInstanceInfo(props.flag!, props.pipelineCode);
    if (isEmpty(data.pipelineInstanceDTO)) return;
    pipelineInstance.value = data.pipelineInstanceDTO;
    pipelineData.value = data.taskList?.map((task: Pipeline.RunningTask) => ({
      ...task,
      elapsedTime: getTime(task.startTime, task.endTime),
    }));
    variableSettings.value = data.variableSettingList;
  } finally {
    loading.value = false;
  }
};

const startUpdatingTaskTime = () => {
  if (isEmpty(pipelineInstance.value) || pipelineInstance.value.pipelineStatus != PipelineStatus.running) return;
  taskTimer = setInterval(() => {
    pipelineData.value.forEach((task: Pipeline.RunningTask) => {
      task.elapsedTime = getTime(task.startTime, task.endTime);
    });
  }, 1000);
};

const startUpdatingPipelineTime = () => {
  if (isEmpty(pipelineInstance.value)) return;
  getContinueTime();
  timer = setInterval(() => {
    getContinueTime();
  }, 1000);
};

const handleAction = async (data: Pipeline.Action) => {
  if (data.actionType === ActionType.button) {
    taskLogModalRef.value.onOpen(data.actionUrl);
  } else if (data.actionType === ActionType.invoke) {
    const res = await defHttp.post({ url: data.actionUrl }, { isTransformResponse: false });
    if (res.code == 200) {
      message.success(res.msg);
    } else {
      message.error(res.msg);
    }
  } else if (data.actionType === ActionType.redirect) {
    // Modal.confirm({
    //   title: '提示',
    //   icon: null,
    //   getContainer: () => runningDetailRef.value,
    //   bodyStyle: { padding: '20px' },
    //   width: 800,
    //   okText: '确认打开',
    //   content:
    //     '确定要打开此文件吗？您正在打开报告上传任务中用户上传的自定义文件，该文件可能危害您的计算机，请仅打开您信任的文件。',
    //   onOk: () => {
    window.open(data.actionUrl);
    //   },
    // });
  }
};

const handleInvoke = async (data: Pipeline.InvokeBtn) => {
  const res = await defHttp.post({ url: data.url }, { isTransformResponse: false });
  if (res.code == 200) {
    message.success(res.message);
  } else {
    message.error(res.message);
  }
};

const environmentVariableModalRef = ref<any>(null);
const handelCheckVariable = () => {
  environmentVariableModalRef.value.showModal();
};

const stop = async () => {
  stopLoading.value = true;
  await stopPipeline(pipelineInstance.value.pipelineCode).finally(() => {
    stopLoading.value = false;
  });
  message.success('终止成功');
};

async function init() {
  await fetchPipelineInfo();
  // 任务计时
  startUpdatingTaskTime();
  // 流水线计时
  startUpdatingPipelineTime();
}

const handleTriggerPipelineSuccess = () => {
  init();
};

defineExpose({
  refresh: init,
});
</script>
<style lang="less" scoped>
::v-deep .ant-spin-container,
.ant-spin-nested-loading {
  height: 100%;
  width: 100%;
}
.running-detail-container {
  height: 100%;
  .empty-dom {
    height: 100%;
    padding: 20px 40px;
    background-color: #fff;
  }
  .header {
    height: 60px;
    background: #ffffff;
    box-shadow: 0px 2px 4px 0px rgba(107, 132, 165, 0.1);
    display: flex;
    align-items: center;
    padding: 0 40px;
    font-size: 14px;
    justify-content: space-between;
    > div {
      display: flex;
      align-items: center;
    }
    .item {
      display: flex;
      align-items: center;
      margin-right: 60px;
      .label {
        color: rgba(0, 0, 0, 0.6);
        font-weight: 400;
      }
      .value {
        color: rgba(0, 0, 0, 0.8);
        font-weight: 400;
      }
      .function-value {
        font-size: 16px;
        color: #2590ff;
        font-weight: 600;
        cursor: pointer;
      }
    }
    .divider {
      height: 14px;
      width: 1px;
      border: 1px solid rgba(231, 239, 248, 1);
      margin: 0 10px;
    }
    .terminate-btn {
      background: #ffffff;
      border: 1px solid rgba(230, 55, 28, 0.7);
      border-radius: 4px;
      height: 32px;
      padding: 9px 16px;
      color: rgba(230, 55, 28, 0.8);
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 4px;
    }
  }
  .content-box {
    height: calc(100% - 60px);
    padding: 0 40px 20px 40px;
    .content {
      height: 100%;
      background: #f3f7fb;
      border-radius: 4px;
      padding: 30px;
      overflow: auto;
      display: flex;
      gap: 50px;
      .task-group {
        .linked-group {
          position: relative;
          &::before {
            content: '';
            position: absolute;
            right: -50px;
            top: 48px;
            height: 1px;
            width: 50px;
            border-bottom: 1px solid rgba(225, 230, 237, 1);
          }
        }
        .lined-item {
          position: relative;
          &::after {
            content: '';
            position: absolute;
            border: 1px solid rgba(225, 230, 237, 1);
            border-right: none;
            border-top-left-radius: 8px;
            border-bottom-left-radius: 8px;
            bottom: -72px;
            left: -20px;
            height: calc(100% + 24px);
            width: 20px;
          }
        }
        .no-radius {
          &::after {
            border-top-left-radius: 0px;
            border-top: none;
            bottom: -70px;
          }
        }
        .task-item {
          margin-bottom: 20px;
          position: relative;
          width: 270px;
          min-height: 118px;
          background: #ffffff;
          border: 1px solid rgba(225, 230, 237, 1);
          border-radius: 4px;
          box-shadow: 0 2px 4px 0 rgba(38, 38, 38, 0.1);
          position: relative;
          &:hover {
            border: 1px solid rgba(37, 144, 255, 0.5);
          }
          .border-preffix {
            position: absolute;
            left: 0;
            top: 0;
            width: 4px;
            height: 100%;
            border-top-left-radius: 4px;
            border-bottom-left-radius: 4px;
            background: #00ab08;
          }
          .top-box {
            padding: 12px 16px 10px 16px;
            height: 65px;
            border-bottom: 1px solid rgba(231, 239, 248, 1);
            .title {
              display: flex;
              align-items: center;
              font-size: 14px;
              color: rgba(0, 0, 0, 0.8);
              font-weight: 500;
              margin-bottom: 8px;
            }
            .time-and-tools {
              display: flex;
              justify-content: space-between;
              font-size: 12px;
              color: rgba(0, 0, 0, 0.6);
              .tool-btn {
                display: flex;
                gap: 16px;
                > div {
                  cursor: pointer;
                  gap: 4px;
                  display: flex;
                  align-items: center;
                }
              }
            }
          }
          .bottom-box {
            padding: 0 16px;
            .error-tip {
              width: 100%;
              height: 50px;
              display: flex;
              align-items: center;
              justify-content: center;
              color: #e6371c;
            }
            .bottom-info {
              display: flex;
              justify-content: space-around;
              align-items: center;
              height: 100%;
              gap: 16px;
              color: rgba(0, 0, 0, 0.6);
              line-height: 14px;
              font-weight: 400;
              font-size: 12px;
              &-item {
                margin: 16px 0;
                display: flex;
                flex-direction: column;
                gap: 2px;
              }
            }
            .invoke {
              display: flex;
              height: 50px;
              align-items: center;
              font-size: 12px;
              justify-content: end;
              gap: 16px;
              > div {
                cursor: pointer;
                display: flex;
                align-items: center;
                gap: 4px;
              }
            }
          }
        }
      }
    }
  }
  :deep(.ant-btn) {
    border-radius: 4px;
    padding: 0 30px;
  }
  :deep(.ant-modal-content) {
    border-radius: 4px;
  }
  :deep(.ant-modal-confirm-content) {
    margin-top: 20px;
  }
}
</style>
